package main

import (
	"fmt"
	"strconv"
	"unsafe"
)

func main() {
	//这种方式相当于直接使用定义了一个变量，使用他的默认值
	var i int = 66
	fmt.Println("i = ", i)

	var f float32 = 12.5
	fmt.Println("f = ", f)

	t := 55
	fmt.Println("t = ", t)

	var aa, bb, cc int
	fmt.Println("aa = ", aa, "bb = ", bb, "cc = ", cc)

	/*
		三种变量的使用方式
			var 变量名 +数据类型(省略数据类型，初始化就必须赋值，自动类型推倒)
			变量名 := 变量值(赋值)
			一次声明多个变量;全局变量声明
				全局变量的声明可以共用一个var
		程序中的+号:
			如果变量是数字类型,做加法
			如果变量是string类型,做字符串拼接
		数据类型:
			基本数据类型:
				数值型:
					int,int8(1字节,Byte),int16(),int32(java中int类型),int64(java中long类型)
					uint(与操作系统有关,32位系统是4字节),uint8(0-255),uint32(0-2^32 -1),uint64(0-2^64 -1),byte
					浮点类型: float32,float64
						单精度: 4字节
						双精度: 8字节
				字符型:
					没有专门的字符型,使用byte来保存单个字母字符
				布尔型:bool
				字符串:string
			复杂数据类型:
				指针: Point
				数组:
				结构体: struct
				管道:channel
				函数: 也是一种类型
				切片: slice
				接口:interface
				map
		查看变量类型和所占字节的大小: unsafe.Sizeof()
	*/
	fmt.Println("gn1 = ", gn1, "gn2 = ", gn2, "gn3 = ", gn3)

	str0 := "Yuan"
	str1 := " Pengyue"
	fmt.Println(str0 + str1)

	var tt int64 = 9000000000000000000
	fmt.Println(tt)
	fmt.Println(unsafe.Sizeof(tt))

	var www uint = 222222222222222222
	fmt.Println("Sizeof(www) = ", unsafe.Sizeof(www))

	var yyy int = 5555555555
	fmt.Println("Sizeof(yyy) = ", unsafe.Sizeof(yyy))

	// 小数点
	f01 := .25622
	f02 := 2.456
	f03 := 3.14159260452123 //默认自动推导到float64,默认浮点数在机器中存放说明：浮点数=符号位+指数位+尾数位(位数位丢失)
	fmt.Println(f01, f02, f03)
	// 字符
	char01 := 'a' //默认不指定类型则使用int4存放一个字符,如果是在ascii中,使用byte
	fmt.Println(unsafe.Sizeof(char01))
	//换行符
	char02 := '\n'
	fmt.Println(char02)
	//Go 语 言 的 字 符 使 用 UTF-8 编码，英文字母-1 个字节 汉字-3 个字节
	//字符的本质是一个整数
	var char03 = 10
	fmt.Printf("chs = %c\n", char03)

	//rune == int32, int == int64
	var rune01 rune = 128
	var int01 int16 = 128
	fmt.Println("sizeOf(rune01): ", unsafe.Sizeof(rune01), "sizeOf(rune02): ", unsafe.Sizeof(int01))

	// 字符本质的讨论
	/*
		1、字符型 存储到 计算机中，需要将字符对应的码值（整数）找出来
		存储：字符—>对应码值---->二进制–>存储
		读取：二进制----> 码值 ----> 字符 --> 读取
		2、字符和码值的对应关系是通过字符编码表决定的(是规定好)
		3、Go 语言的编码都统一成了 utf-8。非常的方便，很统一，再也没有编码乱码的困扰了。
	*/

	// 布尔类型
	/* 布尔类型也叫bool类型，bool类型数据只允许取值true和false
	   2、bool类型占1个子节
	   3、bool类型适用于逻辑运算，一般用于程序流程控制。
	*/
	var bool01 bool = true
	fmt.Println(bool01)

	//字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本
	name := "你好 Golang"
	fmt.Println(name)
	fmt.Printf("type = %T, size = %d\n", name, unsafe.Sizeof(name))

	// 1、Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本，这样 Golang 统一使用 UTF-8 编码,中文乱码问题不会再困扰程序员。
	//2、字符串一旦赋值了，字符串就不能修改字符串内部的元素了；在 Go 中字符串是不可变的。但是可以重新整体赋值。
	//3、字符串的两种表示形式
	//第一、双引号, 会识别转义字符
	//第二、反引号，以字符串的原生形式输出，包括换行和特殊字符，可以实现防止攻击、输出源代码等效果
	var str01 string = "你好\nGolang"
	var str02 string = `
		func main() {
	
		//十进制形式：如 5.12   .512（必须有小数点）
		num1 := 5.12
		num2 := .512
		fmt.Println("num1: ", num1, " num2: ", num2)
		fmt.Printf("num1 type %T: num1 size %d\n", num1, unsafe.Sizeof(num1))
	
		//科学计数法形式
		num3 := 5.1234e2
		num4 := 5.1234E2
		num5 := 5.1234E-2
		fmt.Println("num3: ", num3, " num4: ", num4, " num5: ", num5)
		fmt.Printf("num3 type %T: num3 size %d\n", num3, unsafe.Sizeof(num3))
	
	}
	`
	fmt.Println(str01, str02)

	// 基本数据类型的初始值
	// 整型 浮点型 = 0
	// 字符串 = ""
	// 布尔 = false
	var ii int8
	var ff float32
	var ss string
	var bo bool
	fmt.Println("ii=%v, ff=%v, ss=%v, bo=%v", ii, ff, ss, bo)

	// 基本数据类型 与 字符串的转换
	var ia int8 = 127
	ss = fmt.Sprintf("%d", ia)
	fmt.Printf("ss‘s type is %T", ss)
	//使用strconv
	fmt.Println()
	str := strconv.FormatInt(int64(ia), 10)
	fmt.Printf("ss 's type is %T", str)
	fmt.Println()

	str = "12856"
	var rr int64
	rr, _ = strconv.ParseInt(str, 10, 64)
	fmt.Println(rr)

	//  指针
	/*
		1、基本数据类型，变量存的就是值，也叫值类型。
		2、每个变量都有一个地址，要获取变量的地址，用&，比如：var num int，获取num的地址：&num。
		3、指针类型，指针变量存的是一个地址值，这个地址指向的空间存的才是值。
		比如：var ptr *int = &num(不能直接用一个数值赋给指针变量)
		4、获取指针类型所指向的值，使用：*，比如：var ptr int，使用ptr获取ptr指向空间的存的值。
	*/
	var rse int8 = 127
	var addres *int8 = &rse
	fmt.Println("res的地址是: ", addres)
	var resV int8 = *addres
	fmt.Println("通过地址获取rse的值: ", resV)

	testPtr(&rse)
	fmt.Println("通过指针修改rse的值: ", rse)

	// 值类型和引用类型
	// 值类型: 基本数据类型 int 系列, float 系列, bool, string 、数组和结构体 struct。
	// 变量直接存储值，内存通常在栈中分配。
	// 引用类型: 指针、slice 切片、map、管道 chan、interface。
	//变量存储的是一个地址，这个地址对应的空间才真正存储数据(值)，内存通常在堆上分配，当没有任何变量引用这个地址时，该地址对应的数据空间就成为一个垃圾，由 GC 来回收。

	// 控制台读取用户输入
	//var tmp string
	//fmt.Println("请输入名字")
	//fmt.Scanln(&tmp)
	//fmt.Printf("名字： %s", tmp)
	//var nname string
	//var salary float64
	//var age byte
	//var isPass bool
	//fmt.Scanf("%s %d %f %t", &nname, &age, &salary, &isPass)
	//fmt.Printf("年龄: %v,姓名: %v,薪资: %v,是否通过考试: %v", age, nname, salary, isPass)

	var x int = 4
	result := x << 2
	fmt.Println("result: ", result)

	for i := 1; i < 5; i++ {
		for j := 1; j <= i; j++ {
			if j == 3 {
				goto label
			}
			fmt.Println(" i * j = ", i*j)
		}
	}
label:
	fmt.Println("直接结束....")
}

var (
	gn1 = false
	gn2 = 99
	gn3 = 97
)

func testPtr(num *int8) {
	*num = 16
}
